package cn.rengy.tool.core;


import java.io.Serializable;
import java.sql.Timestamp;
import java.time.Duration;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;



/**
 * 修改 Twitter的Snowflake 算法 毫秒改成秒，扩大了序列号长度 <br>
 * 分布式系统中，全局唯一ID， 简单， 按照时间有序生成。
 * <p>
 * snowflake的结构如下(每部分用-分开):<br>
 * <pre>
 * 
 * 0 - 时间戳 - workerid - nodeid - 序列号
 * 1 - 31   - 10       - 5    - 17
 * </pre>
 * <p>
 * 第一位为未使用(符号位表示正数)，接下来的31位为秒级时间<br>
 * 然后是10位workerid和5位nodeid<br>
 * 最后17位是秒内的计数
 * <p>
 * 可以通过生成的id反推出生成时间,workerId
 * <p>
 * 参考：http://www.cnblogs.com/relucent/p/4955340.html
 */
public class _Snowflake implements Serializable {
	private static final long serialVersionUID = 1L;
	/** 起始时间epoch，单位秒 */
	private final long twepoch;
	/** workerId所占的位数 */
	private final long workerIdBits = 10L;
	/** node所占的位数 */
	private final long nodeIdBits = 5L;
	/** 序列在id中占的位数 */
	private final long sequenceBits = 17L;
	
	/** 时间在id中占的位数 */
	private final long timeBits = 31L;
	
	/** 支持的最大机器节点id (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
	private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
	/** 支持的最大nodeid (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
	private final long maxNodeId = -1L ^ (-1L << nodeIdBits);
	/** 生成序列的掩码 */
	private final long sequenceMask = -1L ^ (-1L << sequenceBits);
	
	/** dataCenter向左移位数 */
    private final long workerIdShift = sequenceBits + nodeIdBits;
	/** 机器ID向左移位数 */
	private final long nodeIdShift = sequenceBits;
	/** 时间截向左移位数 */
	private final long timestampLeftShift = sequenceBits + nodeIdBits + workerIdBits;
	
	private long workerId;
	private long nodeId;
	/** 是否使用内置时钟 */
	private final boolean useSystemClock;
	/** 毫秒内序列 */
	private long sequence = 0L;
	/** 上次生成ID的时间截 */
	private long lastTimestamp = -1L;
	/**
     * 允许时钟回拨的最大时间，单位:毫秒
     */
    private final long maxBackwardMs = 5;
    /**
	 * 构造
	 *
	 * @param workerId     机器ID
	 * @param nodeId         nodeid
	 */
	public _Snowflake(long workerId,long nodeId) {
		this(null,workerId,nodeId, true);
	}

	/**
	 * 构造
	 * @param workerId     机器ID
	 * @param nodeId         nodeid
	 * @param isUseSystemClock 是否使用{@link SnowflakeClock} 获取当前时间戳
	 */
	public _Snowflake(long workerId,long nodeId, boolean isUseSystemClock) {
		this(null, workerId,nodeId, isUseSystemClock);
	}

	/**
	 * @param epochDate        初始化时间起点（null表示默认起始日期）,后期修改会导致id重复,如果要修改连workerId dataCenterId一起修改，慎用
	 * @param workerId         工作机器节点id
	 * @param isUseSystemClock 是否使用{@link SnowflakeClock} 获取当前时间戳
	 */
	public _Snowflake(Date epochDate,long workerId, long nodeId, boolean isUseSystemClock) {
		if (null != epochDate) {
			this.twepoch = Duration.ofMillis(epochDate.getTime()).getSeconds();
		} else{
			//2021-01-17 20:52:56 1610887976000L;
			this.twepoch = Duration.ofMillis(1610887976000L).getSeconds();
			
		}
		if (workerId > maxWorkerId || workerId < 0) {
			throw new IllegalArgumentException("workerId超出范围");
		}
		this.workerId = workerId;
		this.nodeId = nodeId%maxNodeId;//启动1次+1
		this.useSystemClock = isUseSystemClock;
	}
//	public long getMaxWorkerId() {
//		return maxWorkerId;
//	}
	/**
	 * 根据Snowflake的ID，获取WorkerId
	 *
	 * @param id snowflake算法生成的id
	 * @return 所属数据中心
	 */
	public long getWorkerId(long id) {
		return id >> workerIdShift & ~(-1L << workerIdBits);
	}
	/**
	 * 根据Snowflake的ID，获取NodeId
	 *
	 * @param id snowflake算法生成的id
	 * @return 所属机器的id
	 */
	public long getNodeId(long id) {
		return id >> nodeIdShift & ~(-1L << nodeIdBits);
	}

	/**
	 * 根据Snowflake的ID，获取生成时间
	 *
	 * @param id snowflake算法生成的id
	 * @return 生成的时间
	 */
	public long getGenerateDateTime(long id) {
		return (id >> timestampLeftShift & ~(-1L << timeBits)) + twepoch;
	}
	/**
	 * 根据时间戳生成一个id
	 */
	public long createId(long _timestamp) {
		return (Duration.ofMillis(_timestamp).getSeconds() - twepoch) << timestampLeftShift;
	}
	/**
	 * 下一个ID
	 *
	 * @return ID
	 */
	public synchronized long nextId(){
		long timestamp = genTime();
		//依赖系统时钟会出现时钟回拨问题
		if (timestamp < lastTimestamp){
			if((lastTimestamp - timestamp) <= maxBackwardMs){
				LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(maxBackwardMs));
				timestamp = genTime();
				if (timestamp < lastTimestamp){
					// 如果服务器时钟后退 报错。
					throw new IllegalStateException("时钟回拨小于5ms,等待后仍然失败.");
				}
			}else {
				throw new IllegalStateException("时钟回拨超过允许的最大值5ms.");
			}
		}
		//如果是同一时间生成的，则进行毫秒内序列
		if (lastTimestamp == timestamp){
			sequence = (sequence + 1) & sequenceMask;
			//毫秒内序列溢出
			if (sequence == 0) {
				//自旋到下一个毫秒,获得新的时间戳
				timestamp = tilNextMillis(lastTimestamp);
			}
		//时间戳改变，毫秒内序列重置
		} else {
			sequence = 0L;
		}
		lastTimestamp = timestamp;
		//移位并通过或运算拼到一起组成64位的ID
		return ((timestamp - twepoch) << timestampLeftShift) 
				| (workerId << workerIdShift)
				| (nodeId << nodeIdShift) 
				| sequence;
		
	}

	/**
	 * 下一个ID（字符串形式）
	 *
	 * @return ID 字符串形式
	 */
	public String nextIdStr() {
		return Long.toString(nextId());
	}

	// ------------------------------------------------------------------------------------------------------------------------------------ Private method start

	/**
	 * 循环等待下一个时间
	 *
	 * @param lastTimestamp 上次记录的时间
	 * @return 下一个时间
	 */
	private long tilNextMillis(long lastTimestamp) {
		long timestamp = genTime();
		while (timestamp <= lastTimestamp) {
			timestamp = nextTime();
		}
		return timestamp;
	}

	/**
	 * 生成时间戳
	 *
	 * @return 时间戳
	 */
	private long genTime() {
		return this.useSystemClock ? SnowflakeClock.now() : Duration.ofMillis(System.currentTimeMillis()).getSeconds();
	}
	/**
	 * 更快的获取下一个时间
	 */
	private long nextTime() {
		return this.useSystemClock ? SnowflakeClock.getNextTime() : Duration.ofMillis(System.currentTimeMillis()).getSeconds();
	}
	/**
	 * 雪花时钟当前时间
	 * @return
	 */
	public String getSnowflakeClockTime() {
		if(this.useSystemClock){
			return SnowflakeClock.nowDate();
		}
		return null;
	}
	/**
	 * 同步雪花时钟
	 */
	public void syncSnowflakeClockTime() {
		if(this.useSystemClock){
			SnowflakeClock.syncTime();
		}
	}
	// ------------------------------------------------------------------------------------------------------------------------------------ Private method end
	static class SnowflakeClock {
		
		/** 现在时刻的秒数 */
		private volatile long now;

		/**
		 * 构造
		 */
		private SnowflakeClock() {
			this.now = Duration.ofMillis(System.currentTimeMillis()).getSeconds();
			scheduleClockUpdating();
		}

		/**
		 * 开启计时器线程
		 */
		private void scheduleClockUpdating(){
			ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> {
				Thread thread = new Thread(runnable, "Snowflake Clock");
				thread.setDaemon(true);
				return thread;
			});
			scheduler.scheduleAtFixedRate(() -> now++, 1, 1, TimeUnit.SECONDS);
		}

		/**
		 * @return 当前时间毫秒数
		 */
		private long currentTime() {
			return now;
		}
		private long nextTime(){
			return ++now;
		}
		/**
		 * 同步时间
		 */
		private void doSyncTime() {
			long newTime=Duration.ofMillis(System.currentTimeMillis()).getSeconds();
			if(now<newTime){
				now=newTime;
			}
		}
		//------------------------------------------------------------------------ static
		/**
		 * 单例
		 *
		 */
		private static class InstanceHolder {
			public static final SnowflakeClock INSTANCE = new SnowflakeClock();
		}

		/**
		 * 单例实例
		 * @return 单例实例
		 */
		private static SnowflakeClock instance() {
			return InstanceHolder.INSTANCE;
		}

		/**
		 * @return 当前时间
		 */
		public static long now() {
			return instance().currentTime();
		}
		public static long getNextTime() {
			return instance().nextTime();
		}
		/**
		 * @return 当前时间字符串表现形式
		 */
		public static String nowDate() {
			return new Timestamp(instance().currentTime()*1000).toString();
		}
		/**
		 * 同步时间
		 */
		public static void syncTime() {
			instance().doSyncTime();
		}
	}
}
